<?php
/*
 * [JINYUN!] (C)2001-2099 Jinyunweb.com
 * This is NOT a freeware, use is subject to license terms
 
 * 发起 jsapi 支付接口	jsapi_pay
 * 发起 native 支付接口	native_pay
 * 发起 wxapp 支付接口	wxapp_pay
 * 发起 h5 支付接口		h5_pay
 * 发起 app 支付接口	app_pay
 * 发起 付款码 支付接口	micropay
 * 微信支付统一下单接口 create_order
 * 企业付款接口			mch_pay
 * 现金红包接口			redpack
 * 裂变红包接口			grouppack
*/
namespace core;
defined('BY_JYA') or exit('error');
class wechat_pay{
	/*
		初始化接口参数
		如未传入接口参数，则自动从系统支付参数中获取
		
		参数介绍
		$config=array(
			'appid'=>'商户appid',
			'mch_id'=>'商户号',
			'sub_pay'=>'是否服务商支付',
			'sub_mch_id'=>'子商户号',
			'sub_appid'=>'子商户appid',
			'key'=>'商户支付密钥',
			'apiclient_cert'=>'证书cert文件绝对路径',
			'apiclient_key'=>'证书key文件绝对路径',
		);
	*/
	public function __construct($config=array()){
		if($config){
			$this->config=$config;
		}else{
			$setting=uni_setting('pay');
			$this->config=array(
				'appid'=>$setting['wechat_appid'],
				'mch_id'=>$setting['wechat_mchid'],
				'sub_pay'=>$setting['wechat_type'],
				'sub_mch_id'=>$setting['wechat_sub_mchid'],
				'sub_appid'=>$setting['wechat_sub_appid'],
				'key'=>$setting['wechat_miyue'],
				'apiclient_cert'=>ROOT_AT."certs/{$_SESSION['uniacid']}/{$setting['wechat_mchid']}_cert.pem",
				'apiclient_key'=>ROOT_AT."certs/{$_SESSION['uniacid']}/{$setting['wechat_mchid']}_key.pem",
			);
		}
	}
	/*
		jsapi 支付，返回用于发起支付的数组
		参数介绍见 create_order 函数
	*/
	public function jsapi_pay($params){
		$res=$this->create_order($params,'JSAPI');
		$package=array(
			'appId'=>$this->config['appid'],
			'timeStamp'=>"".TIMESTAMP,
			'package'=>'prepay_id='.$res['prepay_id'],
			'signType'=>'MD5',
			'nonceStr'=>random(8),
		);
		$package['paySign']=$this->md5_sign($package);
		return $package;
	}
	/*
		小程序 支付，返回用于发起支付的数组
		参数介绍见 create_order 函数
	*/
	public function wxapp_pay($params){
		if($_SESSION['fromh5wxapp']==1 && $_SESSION['wxappopenid']){
			$params['openid']=$_SESSION['wxappopenid'];
		}
		$res=$this->create_order($params,'JSAPI');
		$package=array(
			'appId'=>$params['appid']?:$this->config['appid'],
			'timeStamp'=>"".TIMESTAMP,
			'package'=>'prepay_id='.$res['prepay_id'],
			'nonceStr'=>random(32),
			'signType'=>'MD5',
		);
		$package['paySign']=$this->md5_sign($package);
		$package['success_url']=$params['success_url'];
		$package['error_url']=$params['error_url'];
		return $package;
	}
	/*
		NATIVE 支付，返回用于生成支付二维码的URL
		参数介绍见 create_order 函数
	*/
	public function native_pay($params){
		$res=$this->create_order($params,'NATIVE');
		return $res['code_url'];
	}
	/*
		H5 支付，返回用于唤起微信支付的URL
		参数介绍见 create_order 函数
	*/
	public function h5_pay($params){
		if(is_numeric($params)){
			$params=pdo_get('core_paylog',array('id'=>$params),array('title','tid','fee'));
		}
		$res=$this->create_order($params,'MWEB');
		return $res['mweb_url'];
	}
	/*
		APP 支付，返回用于APP端发起支付的数组
		参数介绍见 create_order 函数
	*/
	public function app_pay($params){
		$res=$this->create_order($params,'APP');
		$package=array(
			'appid'=>$params['appid']?:$this->config['appid'],
			'partnerid'=>$this->config['mch_id'],
			'prepayid'=>$res['prepay_id'],
			'package'=>'Sign=WXPay',
			'nonceStr'=>random(32),
			'timestamp'=>"".TIMESTAMP,
		);
		$package['sign']=$this->md5_sign($package);
		return $package;
	}
	/*
		统一下单接口
		$params=array(
			'title'=>'商品描述',
			'tid'=>'订单号',
			'fee'=>'订单金额',
			'product_id'=>'产品ID（ NATIVE 时必填）',
		//以下参数为可选参数
			'notify_url'=>'回调地址',
			'appid'=>'公众号或小程序appid',
			'openid'=>'粉丝openid',
		);
	*/
	public function create_order($params,$trade_type){
		$api_url='https://api.mch.weixin.qq.com/pay/unifiedorder';
		$package = array(
			'appid'=>$params['appid']?:$this->config['appid'],
			'mch_id'=>$this->config['mch_id'],
			'nonce_str'=>random(8),
			'body'=>$params['title'],
			'attach'=>$_SESSION['uniacid'],
			'out_trade_no'=>$params['tid'],
			'total_fee'=>$params['fee'] * 100,
			'spbill_create_ip'=>CLIENT_IP,
			'time_start'=>date('YmdHis', TIMESTAMP),
			'time_expire'=>date('YmdHis', TIMESTAMP + 600),
			'notify_url'=>$params['notify_url']?:SITEROOT .'custom/payresult/wechat_notify.php',
			'trade_type'=>$trade_type,
		);
		if($trade_type=='NATIVE'){
			$package['product_id']=$params['product_id'];
			$package['spbill_create_ip']=$_SERVER['SERVER_ADDR'];
		}elseif($trade_type=='JSAPI'){
			$package['openid'] =$params['openid']?:$_SESSION['openid'];
		}elseif($trade_type=='MWEB'){
			$package['scene_info'] =ijson_encode(array(
				'h5_info'=>array(
					'type'=>'Wap',
					'wap_url'=>SITEROOT,
					'wap_name'=>$_SESSION['account']['title'],
				),
			));
		}
		//兼容服务商支付
		if($this->config['sub_pay']){
			$package['sub_mch_id']=$this->config['sub_mch_id'];
			$package['sub_appid']=$this->config['sub_appid'];
			if($package['openid']){
				$package['sub_openid']=$package['openid'];
				unset($package['openid']);
			}
		}
		return $this->request($api_url,$package);
		exi('支付参数设置错误！','error');
	}
	/*
		付款码支付 
		参数介绍
		$params=array(
			'title'=>'商品描述',
			'tid'=>'订单号',
			'fee'=>'订单金额',
			'auth_code'=>'扫描付款码得到的数据',
		);
		返回值：直接返回来自微信支付服务器的数据（已转为数组，且已过滤错误）
	*/
	public function micro_pay($params){
		$api_url='https://api.mch.weixin.qq.com/pay/micropay';
		$package = array(
			'appid'=>$this->config['appid'],
			'mch_id'=>$this->config['mch_id'],
			'nonce_str'=>random(8),
			'body'=>$params['title'],
			'attach'=>$_SESSION['uniacid'],
			'out_trade_no'=>$params['tid'],
			'total_fee'=>$params['fee'] * 100,
			'spbill_create_ip'=>$_SERVER['SERVER_ADDR'],
			'time_start'=>date('YmdHis', TIMESTAMP),
			'time_expire'=>date('YmdHis', TIMESTAMP + 600),
			'auth_code'=>$params['auth_code'],
		);
		return $this->request($api_url,$package);
	}
	/*
		企业付款接口
		参数介绍：
		$params=array(
			'mch_billno'=>'商户订单号',
			'openid'=>'收款用户openid',
			'money'=>'支付金额',
			'remark'=>'备注',
		);
		返回值：直接返回来自微信支付服务器的数据（已转为数组，且已过滤错误）
		文档地址
		https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2
	*/
	public function mch_pay($params){
		$api_url='https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers';
		if(is_numeric($params['openid'])){
			$mid=$params['openid'];
			$params['openid']=model('core/account')->get_openid_by_mid($params['openid'],true,$params['wxgid']);
			if(!$params['openid']){
				exi('找不到会员对应的粉丝！','error');
			}
			$from_os=pdo_getcolumn('core_members',array('id'=>$mid,'uniacid'=>$_SESSION['uniacid']),'from_os');
			if(!$params['wxgid'] && $from_os==1){
				$wxgid=pdo_getcolumn('core_fans',array('openid'=>$params['openid'],'mid'=>$mid,'uniacid'=>$_SESSION['uniacid']),'wxgid');
				$wxapp=pdo_get('core_account_wxapp',array('id'=>$wxgid,'uniacid'=>$_SESSION['uniacid']),array('appid','mchid','paykey'));
				$params['appid']=$wxapp['appid'];
				$params['mch_id']=$wxapp['mchid'];
			}
		}
		$params['appid']=$params['appid']?:$this->config['appid'];
		$params['mch_id']=$params['mch_id']?:$this->config['mch_id'];
		$package=array(
			'mch_appid'=>$params['appid'],
			'mchid'=>$params['mch_id'],
			'nonce_str'=>random(32),
			'partner_trade_no'=>$params['mch_billno'],//商户订单号
			'openid'=>$params['openid']?:$_SESSION['openid'],
			'check_name'=>'NO_CHECK',
			//'re_user_name'=>'',
			'amount'=>intval($params['money']*100),
			'desc'=>$params['remark']?:'猜越多得越多，快来抢！',
			'spbill_create_ip'=>$params['spbill_create_ip']?:$_SERVER['SERVER_ADDR'],
		);
		return $this->request($api_url,$package,true);
		
	}
	/*
		发放裂变红包
		参数介绍：
		$params=array(
			'mch_billno'=>'商户订单号',
			'openid'=>'收红包用户openid',
			'money'=>'支付金额',
			'remark'=>'备注',
			'send_name'=>'商户名称',
			'total_num'=>'红包总数',
			'wishing'=>'祝福语',
			'act_name'=>'活动名称',
		);
		返回值：直接返回来自微信支付服务器的数据（已转为数组，且已过滤错误）
	*/
	public function grouppack($params){
		$api_url='https://api.mch.weixin.qq.com/mmpaymkttransfers/sendgroupredpack';
		$fields=array('mch_billno','openid','money');
		foreach($fields as $key){
			if(!$params[$key]){
				return error('关键参数未设置：'.$key);
			}
		}
		if(is_numeric($params['openid'])){		
			$params['openid']=model('core/account')->get_openid_by_mid($params['openid']);
			if(!$params['openid']){
				exi('找不到会员对应的粉丝！','error');
			}
		}
		$package=array(
			'nonce_str'=>random(32),
			'mch_billno'=>$params['mch_billno'],//商户订单号
			'mch_id'=>$this->config['mch_id'],
			'wxappid'=>$this->config['appid'],
			'send_name'=>$params['send_name']?:$_SESSION['account']['title'],
			're_openid'=>$params['openid'],
			'total_amount'=>intval($params['money']*100),
			'total_num'=>$params['total_num']?:10,
			'amt_type'=>'ALL_RAND',
			'wishing'=>$params['wishing']?:'恭喜发财，大吉大利！',
			'act_name'=>$params['act_name']?:'发红包啦',
			'remark'=>$params['remark']?:'猜越多得越多，快来抢！',
		);
		//兼容服务商支付
		if($this->config['sub_pay']){
			$package['sub_mch_id']=$this->config['sub_mch_id'];
			$package['msgappid']=$this->config['sub_appid'];
			
		}
		return $this->request($api_url,$package,true);
	}
	/*
		发放红包
		参数介绍：
		$params=array(
			'mch_billno'=>'商户订单号',
			'openid'=>'收红包用户openid',
			'money'=>'支付金额',
			'remark'=>'备注',
			'send_name'=>'商户名称',
			'wishing'=>'祝福语',
			'act_name'=>'活动名称',
		);
		返回值：直接返回来自微信支付服务器的数据（已转为数组，且已过滤错误）
	*/
	public function redpack($params){
		$api_url='https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack';
		$fields=array('mch_billno','openid','money');
		foreach($fields as $key){
			if(!$params[$key]){
				return error('关键参数未设置：'.$key);
			}
		}
		if(is_numeric($params['openid'])){		
			$params['openid']=model('core/account')->get_openid_by_mid($params['openid']);
			if(!$params['openid']){
				return error('找不到会员对应的粉丝！');
			}
		}
		$params['send_name']=$params['send_name']?$params['send_name']:$_SESSION['account']['title'];
		$package=array(
			'nonce_str'=>random(32),
			'mch_billno'=>$params['mch_billno'],//商户订单号
			'mch_id'=>$this->config['mch_id'],
			'wxappid'=>$this->config['appid'],
			'send_name'=>$params['send_name']?$params['send_name']:'匿名用户',
			're_openid'=>$params['openid'],
			'total_amount'=>intval($params['money']*100),
			'total_num'=>1,
			'wishing'=>$params['wishing']?:'恭喜发财，大吉大利！',
			'client_ip'=>$_SERVER['SERVER_ADDR'],
			'act_name'=>$params['act_name']?:'发红包啦',
			'remark'=>$params['remark']?:'猜越多得越多，快来抢！',
		);
		//兼容服务商支付
		if($this->config['sub_pay']){
			$package['sub_mch_id']=$this->config['sub_mch_id'];
			$package['msgappid']=$this->config['sub_appid'];
			
		}
		return $this->request($api_url,$package,true,true);
	}
	/*
		发起接口请求
	*/
	private function request($api_url,$package,$ssl=false,$msg=''){
		$package['sign']=$this->md5_sign($package);
		$xml=array2xml($package);
		if($ssl){
			$this->check_cert($msg);
			$extra=array(
				'CURLOPT_SSLCERT'=>$this->config['apiclient_cert'],
				'CURLOPT_SSLKEY'=>$this->config['apiclient_key'],
			);
		}else{
			$extra=array();
		}
		$res=cfc('http')->ihttp_request($api_url,$xml,$extra);
		if(is_error($res)){
			if($msg){
				return error($res['message']);
			}else{
				exi($res['message'].'，请联系管理员设置好参数！','error');
			}
		}
		$res=xml2array($res['content']);
		if($res['return_code']!='SUCCESS'){
			if($msg){
				return error($res['return_msg']);
			}else{
				exi($res['return_msg'].'，请联系管理员设置好参数！','error');
			}
		}
		if($res['result_code']!='SUCCESS'){
			if($msg){
				return error($res['err_code']);
			}else{
				exi($res['err_code'].':'.$res['err_code_des'],'error');
			}
		}
		return $res;
	}
	/*
		检查是否上传证书
	*/
	private function check_cert($msg=''){
		if(!is_file($this->config['apiclient_cert']) || !is_file($this->config['apiclient_key'])){
			if($msg){
				return error('请先上传支付证书');
			}else{
				exi('请先上传支付证书！','error');
			}
		}
		return true;
	}
	/*
		生成签名
	*/
	private function md5_sign($package){
		ksort($package);
		$string = '';
		foreach($package as $key => $v) {
			if(empty($v)){
				continue;
			}
			$string .= "{$key}={$v}&";
		}
		$string .= "key={$this->config['key']}";
		return strtoupper(md5($string));
	}
}